import numpy as np
import random
import copy
import math
import matplotlib.pyplot as plt
import time


# function="RastFunc"
import ObjFunction

'''
一个解向量的生成
'''
class LDDHSIndividual:

    '''
    individual of harmony search algorithm
    '''

    def __init__(self,  vardim, bound, function):
        '''
        vardim: dimension of variables
        bound: boundaries of variables
        '''
        self.vardim = vardim
        self.bound = bound
        self.chrom = np.zeros(self.vardim)
        self.function = function
        self.fitness = 0.

    def generate(self):
        '''
        generate a random chromsome for harmony search algorithm
        '''

        rnd = np.random.random(size=self.vardim)

        for i in range(0, self.vardim):
            self.chrom[i] = self.bound[0, i] + \
                (self.bound[1, i] - self.bound[0, i]) * rnd[i]

    def calculateFitness(self):
        '''
        calculate the fitness of the chromsome
        '''

        f = getattr(ObjFunction, self.function)
        self.fitness = f(
            self.vardim, self.chrom, self.bound)


class LDDHarmonySearch:

    '''
    the class for harmony search algorithm
    '''

    def __init__(self, sizepop, vardim, bound, MAXGEN, HMCR,PAR,function="RastFunc"):
        '''
        sizepop: population sizepop
        vardim: dimension of variables
        bound: boundaries of variables
        MAXGEN: termination condition
        params: algorithm required parameters, it is a list which is consisting of[HMCR, PAR,bw]

        '''

        self.sizepop = sizepop
        self.vardim = vardim
        self.bound = bound
        self.MAXGEN = MAXGEN
        self.HMCR = HMCR
        # self.bw = bw
        self.PAR = PAR
        self.U = []
        self.L = []
        self.population = []
        self.bound1 = [self.bound[0,0],self.bound[1,0]]
        self.fitness = np.zeros((self.sizepop, 1))
        #self.trace = np.zeros((self.MAXGEN, 3))
        self.trace = [[],[],[]]
        self.up = []
        self.low = []
        self.function = function
        self.unmove_L = self.bound[0,0]
        self.unmove_U = self.bound[1,0]
        self.ind = LDDHSIndividual(self.vardim, bound,self.function)

    def initialize(self,bound):
        '''
        initialize the population of hs
        '''

        for i in range(0, self.sizepop):
            # ind = HSIndividual(self.vardim, bound,self.function)
            self.ind.generate()
            self.population.append(self.ind)
        # print("初始化：",self.population[0].chrom)
        # print("===============")

    def evaluation(self):
        '''
        evaluation the fitness of the population   求解向量对应的解
        '''
        for i in range(0, self.sizepop):
            self.population[i].calculateFitness()
            self.fitness[i] = self.population[i].fitness

    def improvise(self,HMCR,PAR,best, worst,U,L):
        '''
        improvise a new harmony     即兴创作
        '''

        ind = LDDHSIndividual(self.vardim, self.bound, self.function)

        c = min(1,3*(1-self.t/self.MAXGEN))
        # U = np.max(self.bound[1])
        # L = np.min(self.bound[0])
        for i in range(0, self.vardim):
            # U = self.bound[1,i]
            # L = self.bound[0,i]
            r1 = random.random()
            r2 = random.random()
            if r1 < HMCR:
                if r2 > PAR:
                    ind.chrom[i] = \
                        self.population[best].chrom[i]
                else:
                    if random.random() > 0.5:
                        ind.chrom[i] = \
                            self.population[best].chrom[i] + \
                            r2 * (self.population[random.randint(0,self.sizepop-1)].chrom[i]-self.population[random.randint(0,self.sizepop-1)].chrom[i]+self.population[random.randint(0,self.sizepop-1)].chrom[i]-self.population[random.randint(0,self.sizepop-1)].chrom[i])


                        ind.chrom[i] = c*ind.chrom[i]

                    else:
                        ind.chrom[i] = \
                            self.population[best].chrom[i] - \
                            r2 * (self.population[random.randint(0,self.sizepop-1)].chrom[i]-self.population[random.randint(0,self.sizepop-1)].chrom[i]+self.population[random.randint(0,self.sizepop-1)].chrom[i]-self.population[random.randint(0,self.sizepop-1)].chrom[i])


                        ind.chrom[i] = c * ind.chrom[i]
                    if ind.chrom[i] <self.unmove_L:
                        ind.chrom[i] = self.unmove_L
                    if ind.chrom[i] >self.unmove_U:
                        ind.chrom[i] = self.unmove_U
            else:
                ind.chrom[i] = L + \
                               (U- L) * random.random()




        ind.calculateFitness()

        return ind



    def update(self, ind):
        '''
        update harmony memory
        '''
        # minIdx = np.argmin(self.fitness)
        # if ind.fitness<self.population[minIdx].fitness:
        #     self.population[minIdx] = ind
        #     self.fitness[minIdx] = ind.fitness

        maxIdx = np.argmax(self.fitness)
        if ind.fitness < self.population[maxIdx].fitness:
            #z = self.population[maxIdx].fitness

            self.population[maxIdx] = ind
            self.fitness[maxIdx] = ind.fitness

            #print(z,"update","to",ind.fitness)



    def solve(self):
        '''
        the evolution process of the hs algorithm
        '''

        print("开始执行ID-HS-LDD")
        print(self.bound[0,0],"===",self.bound[1,1])
        start = time.time()

        self.t = 0
        self.initialize(self.bound)

        U_new = self.bound[1,0]
        L_new = self.bound[0,0]
        self.evaluation()
        self.up.append(U_new)
        self.low.append(L_new)

        bestIndex = np.argmin(self.fitness)
        self.best = copy.deepcopy(self.population[bestIndex])
        self.avefitness = np.mean(self.fitness)

        worstIdx = np.argmax(self.fitness)
        worst = copy.deepcopy(self.population[worstIdx])

        rate = self.fitness[bestIndex] / self.fitness[worstIdx]


        Idx = bestIndex
        while self.t < self.MAXGEN - 1 :
            #print("====第"+str(self.t)+"次迭代====")


            self.t += 1
            if self.t < self.MAXGEN/4:
                HMCR = self.HMCR[0]+ ((self.HMCR[1] - self.HMCR[0]) / self.MAXGEN) * self.t
                PAR = self.PAR[1]
            else:
                HMCR = self.HMCR[1]
                PAR = self.PAR[1] - ((self.PAR[1] - self.PAR[0]) / self.MAXGEN) * self.t


            ind1 = self.improvise(HMCR,PAR,Idx, worstIdx,U_new,L_new)
            self.update(ind1)

            # for j in range(self.vardim):
            #     # MAX = np.argmax(self.best.chrom)
            #     # MIN = np.argmin(self.best.chrom)
            #     MAX = self.population[0].chrom[j]
            #     MIN = self.population[0].chrom[j]
            #     for i in range(self.sizepop):
            #         if self.population[i].chrom[j] > MAX:
            #             MAX = self.population[i].chrom[j]
            #         if self.population[i].chrom[j] < MIN:
            #             MIN = self.population[i].chrom[j]
            #
            #     U_cov = MAX
            #     L_cov = MIN
            #     U_new = (1-self.t/self.MAXGEN)*U_new+(self.t/self.MAXGEN)*U_cov
            #     L_new = (1 - self.t / self.MAXGEN) * L_new + (self.t / self.MAXGEN) * L_cov
            #     self.bound[0,j] = L_new
            #     self.bound[1,j] = U_new
            self.U.clear()
            self.L.clear()
            for i in range(self.sizepop):
                MAX = np.argmax(self.population[i].chrom)
                MIN = np.argmin(self.population[i].chrom)
                self.U.append(self.population[i].chrom[MAX])
                self.L.append(self.population[i].chrom[MIN])
            U_cov = np.max(self.U)
            L_cov = np.min(self.L)

            U_new = (1-self.t/self.MAXGEN)*U_new+(self.t/self.MAXGEN)*U_cov
            L_new = (1 - self.t / self.MAXGEN) * L_new + (self.t / self.MAXGEN) * L_cov


            # MAX = np.tile(self.unmove_L, self.sizepop)
            # MIN = np.tile(self.unmove_U, self.sizepop)
            # for i in range(self.sizepop):
            #     current_max = np.argmax(self.population[i].chrom)
            #     current_min = np.argmin(self.population[i].chrom)
            #     if current_max > MAX[i]:
            #         MAX[i] = current_max
            #     if current_min < MIN[i]:
            #         MIN[i] = current_min
            #
            # final_max = np.argmax(MAX)
            # final_min = np.argmin(MIN)
            # # self.bound[0, :] = final_min
            # # self.bound[1, :] = final_max
            #
            # U_cov = final_max
            # L_cov = final_min
            # U_new = (1-self.t/self.MAXGEN)*U_new+(self.t/self.MAXGEN)*U_cov
            # L_new = (1 - self.t / self.MAXGEN) * L_new + (self.t / self.MAXGEN) * L_cov


            # self.bound[0,:] = L_new
            # self.bound[1,:] = U_new

            self.U.append(U_new)
            self.L.append(L_new)




            #HMCR,PAR = self.decide(sim,self.t/self.MAXGEN)

            # if self.t <self.MAXGEN/2:
            #     Idx = worstIdx
            # else:
            #     Idx = bestIndex
            Idx = bestIndex


            self.up.append(U_cov)
            self.low.append(L_cov)


            bestIndex = np.argmin(self.fitness)
            self.best = self.population[bestIndex]
            self.avefitness = np.mean(self.fitness)

            worstIdx = np.argmax(self.fitness)


            # self.trace[self.t, 0] = self.best.fitness
            # self.trace[self.t, 1] = self.avefitness
            # self.trace[self.t, 2] = worst.fitness
            self.trace[0].append(self.best.fitness)







        print("结束后:",self.population[0].chrom)

        print("=================")

        end = time.time()
        print("time:",end-start)
        print("ID-HS-LDDbest:",self.best.fitness)
        print("=================")


